package agent;

import java.util.List;

import org.snmp4j.agent.DuplicateRegistrationException;
import org.snmp4j.agent.MOGroup;
import org.snmp4j.agent.MOServer;
import org.snmp4j.agent.mo.MOAccessImpl;
import org.snmp4j.agent.mo.MOColumn;
import org.snmp4j.agent.mo.MOFactory;
import org.snmp4j.agent.mo.MOMutableTableModel;
import org.snmp4j.agent.mo.MOScalar;
import org.snmp4j.agent.mo.MOTable;
import org.snmp4j.agent.mo.MOTableIndex;
import org.snmp4j.agent.mo.MOTableIndexValidator;
import org.snmp4j.agent.mo.MOTableModel;
import org.snmp4j.agent.mo.MOTableSubIndex;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.SMIConstants;
import org.snmp4j.smi.Variable;

import agent.monitors.OriginChangeListener;
import agent.monitors.TorrentFile;
import agent.monitors.TorrentMonitor;
import agent.variables.MaxActive;
import agent.variables.MaxDl;
import agent.variables.MaxUl;
import agent.variables.Pex;
import agent.variables.Port;
import agent.variables.fileentry.FileEntryRow;
import agent.variables.fileentry.FileEntryRowFactory;

public class TorrentMIB implements MOGroup, OriginChangeListener
{
	/**
	 * Constants
	 * OID of this MIB module for usage which can be used for its identification.
	 */
	public static final OID oidTorrentMib	= new OID(new int[] {});
	public static final OID oidMaxUl		= new OID(new int[] { 1, 3, 6, 1, 4, 1, 12619, 1, 2, 1, 0 });
	public static final OID oidMaxDl		= new OID(new int[] { 1, 3, 6, 1, 4, 1, 12619, 1, 2, 2, 0 });
	public static final OID oidMaxActive	= new OID(new int[] { 1, 3, 6, 1, 4, 1, 12619, 1, 2, 3, 0 });
	public static final OID oidPex			= new OID(new int[] { 1, 3, 6, 1, 4, 1, 12619, 1, 2, 4, 0 });
	public static final OID oidPort			= new OID(new int[] { 1, 3, 6, 1, 4, 1, 12619, 1, 2, 5, 0 });
	
	private MOScalar<OctetString> maxUl;
	private MOScalar<OctetString> maxDl;
	private MOScalar<OctetString> maxActive;
	private MOScalar<OctetString> pex;
	private MOScalar<OctetString> port;

	public static final OID oidFileEntry		= new OID(new int[] { 1, 3, 6, 1, 4, 1, 12619, 1, 1, 1, 1 });

	public static final OID oidSeedsConnected	= new OID(new int[] { 1, 3, 6, 1, 4, 1, 12619, 1, 1, 1, 1, 12 });

	// Column sub-identifer definitions for fileEntry:
	public static final int colHash				= 1;
	public static final int colStatus			= 2;
	public static final int colName				= 3;
	public static final int colSize				= 4;
	public static final int colPercent			= 5;
	public static final int colDownloaded		= 6;
	public static final int colUploaded			= 7;
	public static final int colUploadSpeed		= 8;
	public static final int colDownloadSpeed	= 9;
	public static final int colEta				= 10;
	public static final int colPeersConnected	= 11;
	public static final int colSeedsConnected	= 12;
	
	// Column index definitions for fileEntry:
	public static final int idxHash				= 0;
	public static final int idxStatus			= 1;
	public static final int idxName				= 2;
	public static final int idxSize				= 3;
	public static final int idxPercent			= 4;
	public static final int idxDownloaded		= 5;
	public static final int idxUploaded			= 6;
	public static final int idxUploadSpeed		= 7;
	public static final int idxDownloadSpeed	= 8;
	public static final int idxEta				= 9;
	public static final int idxPeersConnected	= 10;
	public static final int idxSeedsConnected	= 11;
	
	private MOTable<FileEntryRow, MOColumn<Variable>, MOTableModel<FileEntryRow>>	fileEntry;
	private MOTableModel<FileEntryRow> fileEntryModel;

	/**
	 * Constructs a NEW-MIB instance without actually creating its <code>ManagedObject</code> instances. This has to be done in a sub-class constructor or after construction by calling {@link #createMO(MOFactory moFactory)}.
	 */
	protected TorrentMIB(){
	}
	
	/**
	 * Constructs a NEW-MIB instance and actually creates its <code>ManagedObject</code> instances using the supplied <code>MOFactory</code> (by calling {@link #createMO(MOFactory moFactory)}).
	 * 
	 * @param moFactory
	 *            the <code>MOFactory</code> to be used to create the managed objects for this module.
	 */
	public TorrentMIB(MOFactory moFactory) {
		this();
		createMO(moFactory);
	}

	
	/**
	 * Create the ManagedObjects defined for this MIB module using the specified {@link MOFactory}.
	 * 
	 * @param moFactory
	 *            the <code>MOFactory</code> instance to use for object creation.
	 */
	protected void createMO(MOFactory moFactory) {
		maxUl = new MaxUl(oidMaxUl, moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_WRITE));
		maxDl = new MaxDl(oidMaxDl, moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_WRITE));
		maxActive = new MaxActive(oidMaxActive, moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_WRITE));
		pex = new Pex(oidPex, moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_WRITE));
		port = new Port(oidPort, moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_WRITE));
		createFileEntry(moFactory);
	}
	
	public MOScalar<OctetString> getMaxUl() {
		return maxUl;
	}
	
	public MOScalar<OctetString> getMaxDl() {
		return maxDl;
	}
	
	public MOScalar<OctetString> getMaxActive() {
		return maxActive;
	}
	
	public MOScalar<OctetString> getPex() {
		return pex;
	}
	
	public MOScalar<OctetString> getPort() {
		return port;
	}
	
	public MOTable<FileEntryRow, MOColumn<Variable>, MOTableModel<FileEntryRow>> getFileEntry() {
		return fileEntry;
	}
	
	@SuppressWarnings({ "unchecked" })
	private void createFileEntry(MOFactory moFactory) {
		// Index definition
		MOTableSubIndex[] fileEntryIndexes = new MOTableSubIndex[] { moFactory.createSubIndex(oidSeedsConnected, SMIConstants.SYNTAX_INTEGER, 1, 1),
				moFactory.createSubIndex(oidSeedsConnected, SMIConstants.SYNTAX_INTEGER, 1, 1) };
		
		MOTableIndex fileEntryIndex = moFactory.createIndex(fileEntryIndexes, false, new MOTableIndexValidator() {
			public boolean isValidIndex(OID index) {
				boolean isValidIndex = true;
				return isValidIndex;
			}
		});
		
		// Columns
		MOColumn<Variable>[] fileEntryColumns = new MOColumn[12];
		fileEntryColumns[idxHash] = 			moFactory.createColumn(colHash, 			SMIConstants.SYNTAX_OCTET_STRING, 	moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxStatus] = 			moFactory.createColumn(colStatus, 			SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxName] = 			moFactory.createColumn(colName, 			SMIConstants.SYNTAX_OCTET_STRING, 	moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxSize] = 			moFactory.createColumn(colSize, 			SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxPercent] = 			moFactory.createColumn(colPercent, 			SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxDownloaded] = 		moFactory.createColumn(colDownloaded, 		SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxUploaded] = 		moFactory.createColumn(colUploaded, 		SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxUploadSpeed] = 		moFactory.createColumn(colUploadSpeed, 		SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxDownloadSpeed] = 	moFactory.createColumn(colDownloadSpeed, 	SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxEta] = 				moFactory.createColumn(colEta, 				SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxPeersConnected] = 	moFactory.createColumn(colPeersConnected, 	SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		fileEntryColumns[idxSeedsConnected] = 	moFactory.createColumn(colSeedsConnected, 	SMIConstants.SYNTAX_INTEGER, 		moFactory.createAccess(MOAccessImpl.ACCESSIBLE_FOR_READ_ONLY));
		
		// Table model
		fileEntryModel = moFactory.createTableModel(oidFileEntry, fileEntryIndex, fileEntryColumns);
		((MOMutableTableModel<FileEntryRow>) fileEntryModel).setRowFactory(new FileEntryRowFactory());
		fileEntry = moFactory.createTable(oidFileEntry, fileEntryIndex, fileEntryColumns, fileEntryModel);
	}
	
	public void registerMOs(MOServer server, OctetString context) throws DuplicateRegistrationException {
		server.register(this.maxUl, context);
		server.register(this.maxDl, context);
		server.register(this.maxActive, context);
		server.register(this.pex, context);
		server.register(this.port, context);
		server.register(this.fileEntry, context);
	}
	
	public void unregisterMOs(MOServer server, OctetString context) {
		server.unregister(this.maxUl, context);
		server.unregister(this.maxDl, context);
		server.unregister(this.maxActive, context);
		server.unregister(this.pex, context);
		server.unregister(this.port, context);
		server.unregister(this.fileEntry, context);
	}

	/**
	 * Método executado quando os dados na fonte de dados mudam
	 * 
	 * @param monitor
	 * 			Origem dos dados
	 */
	@Override
	public void originChanged(TorrentMonitor monitor) {
		this.getMaxDl().setValue(new OctetString(monitor.getMaxDl()));
		this.getMaxUl().setValue(new OctetString(monitor.getMaxUl()));
		this.getMaxActive().setValue(new OctetString(monitor.getMaxActive()));
		this.getPex().setValue(new OctetString(monitor.getPex()));
		this.getPort().setValue(new OctetString(monitor.getPort()));

		createFileTable(monitor);
	}
	
	/**
	 * Adiciona os arquivos listados no torrent, primeiro limpando a lista antiga.
	 * 
	 * @param monitor
	 * 			A fonte de informações. É daqui que se extrai as informações do torrent
	 */
	private void createFileTable(TorrentMonitor monitor) {
		List<TorrentFile> fileTable = monitor.getFileEntry();
		if(fileTable != null){
			MOMutableTableModel<FileEntryRow> model = (MOMutableTableModel<FileEntryRow>) this.getFileEntry().getModel();
			
			//Limpa a tabela
			model.clear();
			
			//Busca o proximo OID
			OID lastOID = model.lastIndex();
			if(lastOID == null){
				lastOID = new OID("0");
			}
			
			//Insere as linhas na tabela
			for (TorrentFile fileRow : fileTable) {
				model.addRow(new FileEntryRow((lastOID=lastOID.nextPeer()), fileRow.getVariableArray()));
			}
		}
	}
}
